package com.easypay.demo.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.easypay.demo.utils.RSAUtils;
import com.easypay.demo.utils.SignUtils;
import com.easypay.demo.vo.*;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.Map;

@Service
public class PayService {
    private final static Logger LOGGER = LoggerFactory.getLogger(PayService.class);
    private static final String MEDIA_TYPE = "application/json";

    @Value("${server.host}")
    private String host;

    @Value("${api.public_key}")
    private String publicKey;

    @Value("${api.private_key}")
    private String privateKey;

    @Value("${api.notify_url}")
    private String notifyUrl;

    @Value("${api.auth_id}")
    private String authId;

    @Value("${api.pay_url}")
    private String payUrl;

    @Value("${api.host}")
    private String apiHost;

    @Value("${api.success_url}")
    private String successUrl;

    CloseableHttpClient httpclient;

    private static final String HEADER_SIGN_KEY = "X-Auth-Sign";
    public static final int SUCCESS_RET_CODE = 1001000;

    @Autowired
    RestTemplate restTemplate;

    @PostConstruct
    private void init(){
        httpclient = HttpClients.createDefault();
    }

    public ResultVO<PayInfoVO> pay(RequestOrderVO vo){
        if (vo.getNonceStr() == null){
            vo.setNonceStr(RandomStringUtils.randomAlphabetic(16));
        }
        vo.setAuthId(authId);
        vo.setNotifyURL(host + notifyUrl);
        vo.setSuccessToURL(host + successUrl);
        // 支付宝
        vo.setType(1);

        String signStr = SignUtils.getSignStr(vo);
        String sign =  RSAUtils.signSh256withRSA(privateKey, signStr);

        ResultVO<PayInfoVO> resultVO = this.post(vo, payUrl, new ParameterizedTypeReference<ResultVO<PayInfoVO>>() {
        });
        return resultVO;
    }

    public <C> ResultVO<C> post(Object param, String urlSuffix, ParameterizedTypeReference typeReference){
        // 发送请求
        ResponseEntity<ResultVO<C>> res = null;
        // 响应body
        ResultVO<C> body = null;
        try{
            res = restTemplate.exchange(apiHost + urlSuffix, HttpMethod.POST
                    , new HttpEntity<>(getRequestBody(param), getRequestHeaders(getSign(param)))
                    , typeReference);
            body = res.getBody();
        } catch (final HttpClientErrorException httpClientErrorException) {
            System.out.println("客户端错误");
            TypeReference<ResultVO<C>> typeRef = new  TypeReference<ResultVO<C>>(){};
            return JSON.parseObject(httpClientErrorException.getResponseBodyAsString(), typeRef);
        } catch (HttpServerErrorException httpServerErrorException) {
            System.out.println("服务器错误");
        } catch (Exception exception) {
            exception.printStackTrace();
            System.out.println("意外错误");
        }
        C retData = body.getRetData();
        if(SUCCESS_RET_CODE == body.getRetCode()) {
            // 验证签名
            boolean test = verify(retData, res.getHeaders().toSingleValueMap().get(HEADER_SIGN_KEY));
            LOGGER.info("check sign:{}", test);
        }
        return body;
    }

    private String getRequestBody(Object object) {
        return JSON.toJSONString(object);
    }

    private String getSign(Object object) throws Exception {
        return  RSAUtils.sign(privateKey
                , SignUtils.getSignStr(object)
                , SignUtils.SHA256_WITH_RSA);
    }

    public boolean verify(Map<String, Object> map, String sign){
        String signStr = SignUtils.getSignStr(map);
        return RSAUtils.verifySh256withRSA(publicKey, signStr, sign);
    }

    public boolean verify(Object map, String sign){
        String signStr = SignUtils.getSignStr(map);
        return RSAUtils.verifySh256withRSA(publicKey, signStr, sign);
    }

    /**
     * 测试提现
     */
    public String createDraw(DrawVO vo){
        LOGGER.info("createDraw...", vo);
        vo.setAuthId(authId);
//        vo.setAccount("账号1");
//        vo.setBankName("中国名行");
//        vo.setAccountName("xcc");
        vo.setDescription("测试1");
//        vo.setAmount(new BigDecimal(10));
        vo.setOutDrawNo(RandomStringUtils.randomAlphanumeric(10));
        vo.setNonceStr(RandomStringUtils.randomAlphanumeric(10));
        ResultVO<DrawOrderVO> orderVOResultVO = this.post(vo, "/draw/create", new ParameterizedTypeReference<ResultVO<DrawOrderVO>>() {
        });
        LOGGER.info("result:{}", JSON.toJSONString(orderVOResultVO));
        return JSON.toJSONString(orderVOResultVO);
    }

    public void getPayOrderInfo(){
        RequestOrderInfoVO vo = new RequestOrderInfoVO();
        vo.setAuthId(authId);
        vo.setTransactionId("1477630452854784");
        vo.setNonceStr(RandomStringUtils.randomAlphanumeric(10));
        ResultVO<OrderInfoVO> orderVOResultVO = this.post(vo, "/pay/detail", new ParameterizedTypeReference<ResultVO<OrderInfoVO>>() {
        });
        LOGGER.info("result:{}", JSON.toJSONString(orderVOResultVO));
    }

    public void getDrawInfo(){
        RequestDrawInfoVO vo = new RequestDrawInfoVO();
        vo.setAuthId(authId);
        vo.setDrawNo("1477881111623680");
        vo.setNonceStr(RandomStringUtils.randomAlphanumeric(10));
        ResultVO<DrawOrderVO> orderVOResultVO = this.post(vo, "/draw/detail", new ParameterizedTypeReference<ResultVO<DrawOrderVO>>() {
        });
        LOGGER.info("result:{}", JSON.toJSONString(orderVOResultVO));
    }


    private HttpHeaders getRequestHeaders(String requestSign) {
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.add("Content-Type", MEDIA_TYPE);
        requestHeaders.add("Accept", MEDIA_TYPE);
        requestHeaders.add("X-Auth-Sign", requestSign);
        return requestHeaders;
    }
}
